package com.liz.lizrpccore.templates.excute;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;

import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author liangGongWei
 * @description
 * @create 2021-08-25 21:07
 **/
@Slf4j
public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor {
    /**
     * 保存任务开始执行的时间，当任务结束时，用任务结束时间减去开始时间计算任务执行时间
     */
    private ConcurrentHashMap<String, Date> runnableStartDateMap;
    private String poolName;

    public MonitoredThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                       BlockingQueue<Runnable> workQueue, ExecutorServiceComponent.MyThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        this.poolName = threadFactory.getPoolName();
        this.runnableStartDateMap = new ConcurrentHashMap<String, Date>(maximumPoolSize * 10);

    }

    @Override
    public void shutdown() {
        // 统计已执行任务、正在执行任务、未执行任务数量
        log.info("{} Going to shutdown. Executed tasks: {}, Running tasks: {}, Pending tasks: {}", this.poolName,
                this.getCompletedTaskCount(), this.getActiveCount(), this.getQueue().size());
        super.shutdown();
    }

    @Override
    public List<Runnable> shutdownNow() {
        log.info("{} Going to immediately shutdown. Executed tasks: {}, Running tasks: {}, Pending tasks: {}",
                this.poolName, this.getCompletedTaskCount(), this.getActiveCount(), this.getQueue().size());
        return super.shutdownNow();
    }

    // 任务执行之前，记录任务开始时间
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        String runnableId = String.valueOf(r.hashCode());
        runnableStartDateMap.put(runnableId, new Date());
        log.info("Runnable={},开始执行！ ", runnableId);
    }
    //请注意线程池中的Runnable如果有异常，是不输出错误日志的，必须重写afterExecute方法，自定义处理
    // 任务执行之后，计算任务结束时间
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                Object result = ((Future<?>) r).get();
                String runnableId = String.valueOf(r.hashCode());
                Date startDate = runnableStartDateMap.remove(runnableId);
                Date finishDate = new Date();
                long elapsedTimeInMS = finishDate.getTime() - startDate.getTime();

                log.info(
                        "Runnable={},结束执行, "
                                + "执行耗时: {} ms, ActiveThreadNum: {}, CurrentPoolSize: {}, CorePoolSize: {},MaximumPoolSize: {},LargestPoolSize: {},Task-Completed: {},"
                                + "Task-In-Queue: {}, Task-Total: {},  Thead-KeepAliveTime: {}s",
                        runnableId, elapsedTimeInMS, this.getActiveCount(), this.getPoolSize(), this.getCorePoolSize(),
                        this.getMaximumPoolSize(), this.getLargestPoolSize(), this.getCompletedTaskCount(),
                        this.getQueue().size(), this.getTaskCount(), this.getKeepAliveTime(TimeUnit.SECONDS));
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            log.error("任务异常", t);
            System.err.println(ExceptionUtils.getMessage(t));
        }

    }

}
